#version 330 core
out vec4 FragColor;

struct Material {
    sampler2D texture_diffuse1;
    sampler2D texture_specular1;
    float shiniess;
};

struct Light {
    vec3 position;
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    
    vec3 atte;
    float cutoff;
    float cutoffOut;
};

struct DirectLight {
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
struct PointLight {
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    
    vec3 atte;
};
struct SpotLight {
    vec3 position;
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    
    vec3 atte;
    float cutoff;
    float cutoffOut;
};

in vec3 normal;
in vec3 fragPos;
in vec2 texCoord;

uniform vec3 viewPos;
uniform Material material;

uniform DirectLight directLight;
uniform SpotLight spotLight;
#define POINT_LIGHT_NUM 4
uniform PointLight pointLights[POINT_LIGHT_NUM];

vec3 calcDirectLight(DirectLight light, vec3 normal, vec3 viewDir);
vec3 calcPointLight(PointLight light, vec3 normal, vec3 viewDir);
vec3 calcSpotLight(SpotLight light, vec3 normal, vec3 viewDir);

void main()
{

    vec3 viewDir = normalize(viewPos - fragPos);
    vec3 normal_ = normalize(normal);
    
    vec3 result = calcDirectLight(directLight, normal_, viewDir);
    for (int i = 0; i < POINT_LIGHT_NUM; ++i) {        
        // result += calcPointLight(pointLights[i], normal_, viewDir);
    }
    
    result += calcSpotLight(spotLight, normal_, viewDir);
    FragColor = vec4(result, 1.0);
}


vec3 calcDirectLight(DirectLight light, vec3 normal, vec3 viewDir){
    vec3 amibient = light.ambient * texture(material.texture_diffuse1, texCoord).rgb;
    
    float diff = max(dot(normalize(-light.direction), normal), 0.0);
    vec3 diffuse = light.diffuse * diff * texture(material.texture_diffuse1, texCoord).rgb;
    
    vec3 reflectDir = normalize(reflect(light.direction, normal));
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shiniess);
    vec3 specular = light.specular * spec * texture(material.texture_specular1, texCoord).rgb;
    
    return amibient + diffuse + specular;
}

vec3 calcPointLight(PointLight light, vec3 normal, vec3 viewDir) {

    vec3 amibient = light.ambient * texture(material.texture_diffuse1, texCoord).rgb;
    
    vec3 lightDir = normalize(fragPos - light.position);
    float diff = max(dot(-lightDir, normal), 0.0);
    vec3 diffuse = light.diffuse * diff * texture(material.texture_diffuse1, texCoord).rgb;
    
    vec3 reflectDir = normalize(reflect(lightDir, normal));
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shiniess);
    vec3 specular = light.specular * spec * texture(material.texture_specular1, texCoord).rgb;
    
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.atte.x + light.atte.y * distance +  light.atte.z * (distance * distance));
    
    return (amibient + diffuse + specular) * attenuation;
}

vec3 calcSpotLight(SpotLight light, vec3 normal, vec3 viewDir) {
    vec3 amibient = light.ambient * texture(material.texture_diffuse1, texCoord).rgb;
    
    float diff = max(dot(-light.direction, normalize(normal)), 0.0);
    vec3 diffuse = light.diffuse * diff * texture(material.texture_diffuse1, texCoord).rgb;
    
    vec3 reflectDir = normalize(reflect(light.direction, normal));
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shiniess);
    vec3 specular = light.specular * spec * texture(material.texture_specular1, texCoord).rgb;
    
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.atte.x + light.atte.y * distance +  light.atte.z * (distance * distance));
    
    float theta = dot(normalize(fragPos-light.position), light.direction);
    float intensity = clamp((theta - light.cutoffOut) / (light.cutoff - light.cutoffOut), 0.0, 1.0);
    
    vec3 result = amibient + (diffuse + specular) * intensity;

    return result * attenuation;
}